#ifndef WOLF_CONSOLE_HPP
#define WOLF_CONSOLE_HPP

#include "common.hpp"
#include "Manager.hpp"
#include "font.hpp"
#include "ConsoleMessage.hpp"
#include <deque>
#include <queue>

namespace wolf {
class ConsoleApp;
using namespace std;
/**
 * @brief A simple console
 * @ingroup consoleMod
 *
 *
 * Useful for debug or simple console/ cheater options.
 **/
class WOLF_DECLSPEC Console: public Manager {
public:
	/**
	 * @brief Creates a new Console
	 *
	 *
	 **/
	Console(unsigned nLines, unsigned nCols, Font *font);

	/**
	 * @brief Destructor
	 **/
	~Console();

	void onDraw(Engine &engine, SDL_Surface *dst);
	bool onEvent(Engine &engine, SDL_Event &event);
	void onStartFrame(Engine &engine);
	void onFrame(Engine &engine);

	/**
	 * @brief returns if it is listening input
	 * If it is true, every input key is gathered and stores on
	 *  input;
	 * @see listen(bool), inbuffer(void), inbuffer(const string &)
	 **/
	bool isListening(void) const;

	/**
	 * @brief Sets the listen mode
	 * If it is true, every input key is gathered and stores on
	 *  input;
	 **/
	void setListening(bool value);

	/**
	 * @brief Returns If it is showed on screen
	 *
	 *
	 **/
	bool isShowing(void) const;

	/**
	 * @brief Turn Active or inactive the showing of Console on screen
	 *
	 *
	 **/
	void setShowing(bool value);

	/**
	 * @brief Return the lines number on this console
	 *
	 *
	 **/
	unsigned getLinesCount(void) const;

	/**
	 * @brief Return the columns number on this console
	 *
	 *
	 **/
	unsigned getColumnsCount(void) const;

	/**
	 * @brief Gets the offsetX
	 **/
	int getOffsetX(void) const;

	/**
	 * @brief Sets the offsetX
	 **/
	void setOffsetX(int val);

	/**
	 * @brief Gets the offsetY
	 **/
	int getOffsetY(void) const;

	/**
	 * @brief Sets the offsetY
	 **/
	void setOffsetY(int val);
	/**
	 * @name Direct console manipulation.
	 *
	 * These methods manipulates the input and output the console directly.
	 * @warning If you have ConsoleApp running on this console, you should not call any of
	 *  that methods. These methods are called from ConsoleApp methods, but they are not
	 *  Thread safe themselves.
	 * @{
	 **/

	/**
	 * @brief Gets the carret's current line.
	 *
	 *
	 **/
	unsigned getCursorLine(void) const;

	/**
	 * @brief Sets the carret's current line.
	 *
	 *
	 **/
	void setCursorLine(unsigned value);

	/**
	 * @brief Gets the carret's column
	 *
	 *
	 **/
	unsigned getCursorColumn(void) const;

	/**
	 * @brief Sets the carret's column
	 *
	 *
	 **/
	void setCursorColumn(unsigned value);

	/**
	 * @brief Gets the font
	 *
	 *
	 **/
	Font *getFont(void) const;

	/**
	 * @brief Sets the font
	 *
	 *
	 **/
	void setFont(Font *value);

	/**
	 * @brief Gets the input char
	 *
	 * Every time a key is pressed, this value is stored here.
	 *
	 **/
	int getInput(void) const;

	/**
	 * @brief Sets the input
	 *
	 * It is useful to mark the input as read (like set to \\0), to
	 *  know when it changes
	 **/
	void setInput(int value);

	/**
	 * @brief Gets The caret color
	 *
	 *
	 **/
	const Color & getColor(void) const;

	/**
	 * @brief Sets the caret Color
	 *
	 *
	 **/
	void setColor(const Color &value);

	/**
	 * @brief Writes a char on console
	 *
	 *
	 **/
	void write(int c);

	/**
	 * @brief writes a string on console
	 *
	 *
	 **/
	void write(const char *s);

	/**
	 * @brief writes a string on console
	 *
	 *
	 **/
	void write(const string &s);

	/**
	 * @brief Clears the console
	 *
	 *
	 **/
	void clear(void);

	/**
	 * @brief Removes the first line and adds a new line on bottom
	 *
	 *
	 **/
	void newLine(void);
	/**
	 * @}
	 **/

	/**
	 * @name Console manipulation framework.
	 *
	 * These methods manipulates are responsible to call ConsoleApps (a special
	 *  type of thread) that runs like old console program (it abstracts
	 *  program loop, looking like a simple input-process-output program).
	 * @{
	 **/

	/**
	 * @brief Calls an ConsoleApp
	 *
	 * The console App will run in a different thread. Internally it
	 *  uses a SDL_mutex to synchronize with this console. So if you
	 *  do not use any global or static class function or attribute, it
	 *  should run ok. You also can't run more than one app at time. But
	 *  you can use push some event from that to be enable to pass a message
	 *  to outside.
	 *
	 * This method (and also this class) are not intended to be used on the
	 *  main execution of the game, but to help the development and debugging
	 *  of the game. You really can use it as you want (may be useful as cheat
	 *  console) but you will pay a performance cost for it.
	 *
	 * After call this method, you should not to call @ref DirMani methods. The
	 *  ConsoleApp call them from its methods. Although the ConsoleApp methods
	 *  are thread safe, the @ref DirMani methods are not. So weird thing can
	 *  happen from it.
	 *
	 * @param app The given app.
	 * @return true if the app started to run, false if it fail.
	 **/
	bool callApp(ConsoleApp &app);

	/**
	 * @brief Just Kill the current thread
	 *
	 * Avoid use this. It can produce weird results.
	 **/
	void killApp(void);

	/**
	 * @brief Just wait the current thread
	 *
	 * @return The return value of the app
	 **/
	int waitApp(void);

	/**
	 * @brief Retrives the message
	 *
	 * If there is a message returns it. The message must to be freed by the user.
	 *  The messages are put in a queue, so you can have more than one message.
	 *  Because that we recommend use this code's piece on the frame event to check it:
	 * @code
	 *   while( (ConsoleMessage *message = console.receiveMessage()) != NULL)
	 *   {
	 *       switch(message->type())
	 *       {
	 *       case MESSAGE_TYPE1:
	 *           <handle type1>
	 *           break;
	 *       case MESSAGE_TYPE2:
	 *           <handle type2>
	 *           break;
	 *       default:
	 *           <error handling>
	 *       }
	 *   }
	 * @endcode
	 *
	 * @return The message or NULL if there isn't a message.
	 **/
	ConsoleMessage *receiveMessage(void);

	/**
	 * @brief Send a message to the current running application
	 *
	 * This message must be created on heap (shouldn't be in heap)
	 *  and shouldn't be manipulated by the app after it was sent.
	 **/
	void sendMessage(ConsoleMessage *message);

	///@}
private:
	struct _CharInfo {
		char character;
		Color color;
		_CharInfo() :
				character(0), color(224, 224, 224) {
		}

	};
	bool listening;
	bool showing;
	unsigned linesCount;
	unsigned columnsCount;
	int offsetX;
	int offsetY;
	unsigned cursorLine;
	unsigned cursorColumn;
	Color color;
	Font *font;
	deque<_CharInfo*> outbuffer;
	int input;
	SDL_Thread *appThread;
	SDL_mutex *consoleLock;
	queue<ConsoleMessage*> mainQueue;
	queue<ConsoleMessage*> appQueue;
	friend class ConsoleApp;
};

}

#endif // WOLF_CONSOLE_HPP
